/**
 * @author Eric Jahn Course: Object Oriented Programming Final Project Due Date:
 * August 3, 2012
 */
package oop.chat.connection;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import oop.chat.data.ConverseDB;
import oop.chat.data.Message;
import oop.chat.user.Category;
import oop.chat.user.Contact;
import oop.chat.user.UserAccount;

/**
 * enumeration type to determine the type of the request
 *
 * @author Eric Jahn
 */
public enum RequestType
{
    ADD_CONTACT
    {
        /**
         * add a new contact to an existing contact list
         *
         * @param request
         */
        @Override
        public void performRequest(Request request)
        {
            /**
             * user object was updated with Contact info on the client side,
             * just update the account in the converseDb and the clientThread
             */
            UserAccount user = request.getUserAccount();
            ArrayList<Object> parameter = request.getParameter();
            ClientThread clientThread = (ClientThread) parameter.get(0);
            clientThread.getServer().getConverseDb().addUserAccount(user);
            clientThread.setUser(user);
        }

        /**
         * instantiate and populate the request object, before sending
         *
         * @param user
         * @param userInput
         * @return
         */
        @Override
        public Request prepareRequest(UserAccount user, String userInput)
        {
            ArrayList<Object> parameters = new ArrayList<>();
            String contactElements[] = userInput.split(",", 6);
            Category listCategory = null;
            for (Category c : Category.values())
            {
                /**
                 * if the first element names a special list, save it and remove
                 * it from the array
                 */
                if (c.toString().equals(contactElements[0]))
                {
                    listCategory = Category.valueOf(contactElements[0]);
                    contactElements = Arrays.copyOfRange(contactElements, 1,
                                                         contactElements.length);
                    break;
                }
            }
            Contact newContact = new Contact(contactElements);
            
            if (listCategory != null)
            {
                // Add contact to special list
                user.addContact(newContact, listCategory);
            }
            else
            {
                //Add contact to the contact list
                user.addContact(newContact);
            }

            return new Request(ADD_CONTACT, user, parameters);
        }
    },
    SEND_MESSAGE
    {
        /**
         * send message to destination user
         *
         * @param request
         */
        @Override
        public void performRequest(Request request)
        {
            UserAccount user = request.getUserAccount();
            ArrayList<Object> parameter = request.getParameter();
            /*
             * get object from paramter array instance 0 -> message object 1 ->
             * clientThread object
             */
            Message message = (Message) parameter.get(0);
            ClientThread clientThread = (ClientThread)parameter.get(1);
            if(!clientThread.isLoggedIn())
            {
                /**
                 * if user is not logged in, ask user to login
                 */
                
                Message login = new Message(Server.LOGIN_MESSAGE, Server.ADMIN, user.getContactInfo());

				//send the message to the client
                clientThread.sendMessage(login);
            }
            else
            {
                /**
                 * get destination information and retrieve the destination
                 * clientThread instance from the server
                 */
                Contact destination = message.getDestination();
                ClientThread destinationThread = clientThread.getServer().getClientThread(destination.getPID());

                //send message object to the destination
                if(destinationThread != null && destinationThread.getUser().isLoggedIn())
                {
                    destinationThread.sendMessage(message);
                }
                // tell user destination is not online
                else
                {
                    Message destOffLine = new Message(destination.getFullName() + " is not online", Server.ADMIN, user.getContactInfo());

					//send message to the client
                    clientThread.sendMessage(destOffLine);
                }
            }
        }

        /**
         * instantiate and populate the request object, before sending
         *
         * @param user
         * @param userInput
         * @return
         */
        @Override
        public Request prepareRequest(UserAccount user, String userInput)
        {
            ArrayList<Object> parameters = new ArrayList<>();
            String msgElements[] = userInput.split(",", 2);
            if (msgElements.length != 2)
            {
                // TODO error handling
            }
			/**
			 * TODO implement special list category
			 */
            Contact destination = user.getContactListByCategory(Category.COMMON_LIST).getContact(msgElements[0]);
            Message message = new Message(msgElements[1], user, destination);
            parameters.add(message);
            return new Request(SEND_MESSAGE, user, parameters);
        }
    },	//message, source, destination (contact), timestamp
    LOGIN
    {
        /**
         * login to server
         *
         * @param request
         */
        @Override
        public void performRequest(Request request)
        {
            ArrayList<Object> parameter = request.getParameter();
            UserAccount user = request.getUserAccount();
            /*
             * get object from paramter array instance 
			 * 0 -> password object 
			 * 1 -> clientThread object
             */
            String password = ConverseDB.md5Password((String)parameter.get(0));
            ClientThread clientThread = (ClientThread)parameter.get(1);

            ConverseDB database = clientThread.getServer().getConverseDb();
            // TODO validate password format
            /**
             * this is out of professor's server example, but if not using
             * ConverseDB, where is this database? Without ConverseDb, Server
             * has no prior knowledge of User, so has nothing to compare
             * password to
             */
			String PID = user.getContactInfo().getPID();
            UserAccount dbAccount = database.getUserAccount(PID);
			boolean isLoggedIn = false;
			//check if it is the first login from the user
			String userPassword = dbAccount.getPassword();
			if(userPassword.length() == 0)
			{
				PrintWriter printWriter = null;
				try 
				{
					//safe password to file
					String filename = PID + ".txt";
					printWriter = new PrintWriter(filename);
					printWriter.print(password);
					//set password
					userPassword = password;
					dbAccount.setPassword(password);
					//change login status
					isLoggedIn = true;
				} 
				catch(FileNotFoundException e) 
				{
					e.printStackTrace();
				}
				finally
				{
					printWriter.close();
				}				
			}
			else if(userPassword.equals(password))
            {               
				isLoggedIn = true;
            }
			
			clientThread.setLogin(isLoggedIn);
			clientThread.sendMessage(isLoggedIn);			
        }

        /**
         * instantiate and populate the request object, before sending
         *
         * @param user
         * @param userInput
         * @return
         */
        @Override
        public Request prepareRequest(UserAccount user, String userInput)
        {
            ArrayList<Object> parameters = new ArrayList<>();
            // password
            parameters.add(userInput);
            return new Request(LOGIN, user, parameters);
        }
    },			//user account, password
    DISCONNECT
    {
        /**
         * disconnect from server
         *
         * @param request
         */
        @Override
        public void performRequest(Request request)
        {
        }

        /**
         * instantiate and populate the request object, before sending
         *
         * @param user
         * @param userInput
         * @return
         */
        @Override
        public Request prepareRequest(UserAccount user, String userInput)
        {
            return new Request(DISCONNECT, user, null);
        }
    },
    SHOW_USERS
    {
        /**
         * request server to send back list of users
         *
         * @param request
         */
        @Override
        public void performRequest(Request request)
        {
            UserAccount user = request.getUserAccount();
            ArrayList<Object> parameter = request.getParameter();
            /*
             * get object from paramter array instance 0 -> clientThread object
             */
            ClientThread clientThread = (ClientThread) parameter.get(0);
			
            Message message = null;
            if (!clientThread.isLoggedIn())
            {
                /**
                 * if user is not logged in, ask user to login
                 */
                message = new Message(Server.LOGIN_MESSAGE, Server.ADMIN, user.getContactInfo());

            }
            else
            {
                String users = clientThread.getServer().getConverseDb().listUserAccounts();

                message = new Message(users, Server.ADMIN, user.getContactInfo());

            }

            clientThread.sendMessage(message);
        }
    

        /**
        * instantiate and populate the request object, before sending
        *
        * @param user
        * @param userInput
        * @return
        */
        @Override
        public Request prepareRequest(UserAccount user, String userInput)
        {
            /**
             * Setting parameters here, 
             * because setting them in Request.addParameters isn't working
             */
            ArrayList<Object> parameters = new ArrayList<>();
            return new Request(SHOW_USERS, user, parameters);
        }
   
    },
    SEARCH
	{
        @Override
        public void performRequest(Request request)
        {
            UserAccount user = request.getUserAccount();
            ArrayList<Object> parameter = request.getParameter();
            /*
             * get object from paramter array instance 0 -> clientThread object
             */
            ClientThread clientThread = (ClientThread) parameter.get(1);
			
            Message message = null;
            if(!clientThread.isLoggedIn())
            {
                /**
                 * if user is not logged in, ask user to login
                 */
                message = new Message(Server.LOGIN_MESSAGE, Server.ADMIN, user.getContactInfo());

            }
            else
            {
                String users = (String)parameter.get(0);                     

                message = new Message(users, Server.ADMIN, user.getContactInfo());
            }

            clientThread.sendMessage(message);
        }

        /**
         * instantiate and populate the request object, before sending
         *
         * @param user
         * @param userInput
         * @return
         */
        @Override
		public Request prepareRequest(UserAccount user, String userInput) 
		{
			Collection<Contact> searchlist = user.getContactLists()[Category.COMMON_LIST.getIndex()].getUserContacts().values();
			Contact[] search = searchlist.toArray(new Contact[searchlist.size()]);
			ArrayList<Contact> searchresults = new ArrayList<>();
			for (int i = 0; i < search.length; i++) {
				if (search[i].toString().contains(userInput)) {
					searchresults.add(search[i]);
				}
			}
			ArrayList<Object> parameters = new ArrayList<>();

			parameters.add(searchresults.toString());

			return new Request(SEARCH, user, parameters);
		}
	};
 
    /**
    * Called by the clientThread in the Server to process the request
    *
    * @param request
    */
    public abstract void performRequest(Request request);

    /**
     * Called by the client, to prepare the request to be sent
     *
     * @param user
     * @param userInput
     * @return
     */
    public abstract Request prepareRequest(UserAccount user, String userInput);
}